package vcsn;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Scanner;
import java.util.Vector;

import vcsn.Config.HeaderType;
import vcsn.entities.PeerList;
import vcsn.entities.VectorClock;
import vcsn.utils.Util;


public class ClientThread {
	private File packageFile;
	public boolean flushSuccess;
	public ClientThread(){
		packageFile = Util.createTempFile();
		flushSuccess = false;
	}
	
//	public ClientThread(File sentFile, String projectName, VectorClock localClock, File peerFile, int type){
//		try {
//			PeerList peerListHandler = new PeerList();
//			Vector<String> peerList = peerListHandler.getPeerList(peerFile.getAbsolutePath());
//			for(String peer : peerList){
//				Socket s = new Socket(peer, 14428);
//					
//				long checksum = 0;
//				byte[] stringBytes;
//				FileInputStream inputStream = new FileInputStream(sentFile);
//				
//				File tmp = Util.createTempFile();
//
//				DataOutputStream out = new DataOutputStream(new FileOutputStream(tmp));
//				out.writeInt(type);
//				
//				// Write Project Name
//				stringBytes = projectName.getBytes(Config.charSet);
//				out.writeInt(stringBytes.length);
//				out.write(stringBytes, 0, stringBytes.length);
//
//				//Write localclock string
//				stringBytes = localClock.toString().getBytes(Config.charSet);
//				out.writeInt(stringBytes.length);
//				out.write(stringBytes, 0, stringBytes.length);
//				
//				out.writeLong(sentFile.length());
//				for(long i = 0; i < sentFile.length();i++){
//					out.write(inputStream.read());
//				}
//				out.flush();
//				
//				inputStream.close();
//				checksum = out.size();
//				out.close();
//				
//				inputStream = new FileInputStream(tmp);
//				out = new DataOutputStream(s.getOutputStream());
//
//				out.writeLong(checksum);
//				out.flush();
//				int bb;
//				
//				while((bb = inputStream.read()) >= 0) {
//					out.writeByte(bb);
//				}
//				out.flush();
//
//				out.close();
//				inputStream.close();
//				tmp.delete();
//				s.close();
//			}
////			System.out.println("Prefer Sending: " + peerList.size() + " / Actual Sending: " + counter);
//		} catch (UnknownHostException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//	}
	
	public void writeHeaderIntoPackageFile(String projectName, VectorClock localClock, String senderAddr, int type){
		try {
			DataOutputStream dataOut = new DataOutputStream(new FileOutputStream(packageFile, true));
			// Write Header Type
			dataOut.writeInt(type);

			// Write Project Name
			byte[] stringBytes = projectName.getBytes(Config.charSet);
			dataOut.writeInt(stringBytes.length);
			dataOut.write(stringBytes, 0, stringBytes.length);

			//Write local clock string
			stringBytes = localClock.toString().getBytes(Config.charSet);
			dataOut.writeInt(stringBytes.length);
			dataOut.write(stringBytes, 0, stringBytes.length);
			
			//Write sender address
			stringBytes = senderAddr.getBytes(Config.charSet);
			dataOut.writeInt(stringBytes.length);
			dataOut.write(stringBytes, 0, stringBytes.length);			
			
			dataOut.flush();
			dataOut.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void writeAcknowledgmentHeaderIntoPackageFile(int acknowledgmentType){
		try {
			DataOutputStream dataOut = new DataOutputStream(new FileOutputStream(packageFile, true));
			// Write  Acknowledgment Type
			dataOut.writeInt(acknowledgmentType);
			
			dataOut.flush();
			dataOut.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void writeIntergerIntoPackageFile(int i){
		try {
			DataOutputStream dataOut = new DataOutputStream(new FileOutputStream(packageFile, true));
			// Write  Acknowledgment Type
			dataOut.writeInt(i);
			
			dataOut.flush();
			dataOut.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void writeFileIntoPackageFile(File contentFile){
		try {
			DataOutputStream dataOut = new DataOutputStream(new FileOutputStream(packageFile, true));
			FileInputStream fis = new FileInputStream(contentFile);
			dataOut.writeLong(contentFile.length());
			for(long i = 0; i < contentFile.length();i++){
				dataOut.write(fis.read());
			}
			dataOut.flush();
			fis.close();
			dataOut.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void writeStringIntoPackageFile(String str){
		try {
			DataOutputStream dataOut = new DataOutputStream(new FileOutputStream(packageFile, true));
			
			byte[] stringBytes = str.getBytes(Config.charSet);
			dataOut.writeInt(stringBytes.length);
			dataOut.write(stringBytes, 0, stringBytes.length);
			
			dataOut.flush();
			dataOut.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void flush(File peerFile){
		int cnt = 0;
		PeerList peerListHandler = new PeerList();
		Vector<String> peerList = peerListHandler.getPeerList(peerFile.getAbsolutePath());
		String localAddress = getLocalAddress(peerFile);
		for(String peer : peerList){
			try {
				
				if(peer.equals(localAddress) || peer.equals(Config.localHost)){
					continue;
				}
				flush(peer);
				flushSuccess = true;
			}catch(Exception e){
				e.printStackTrace();
				cnt++;
				if(cnt == peerList.size()){
					flushSuccess = false;
				}
				continue;
			}
		}
	}
	
	public void flush(String destination) throws Exception {
			Socket s = new Socket(destination, 14428);
			long checksum = 0;
			
			FileInputStream is = new FileInputStream(packageFile);
			DataOutputStream out = new DataOutputStream(s.getOutputStream());
			checksum = packageFile.length();
			System.out.println("Sent checksum : " + checksum);
			out.writeLong(checksum);
			out.flush();
			int b;
			
			while((b = is.read()) >= 0) {
				out.writeByte(b);
			}
			out.flush();

			out.close();
			is.close();
			s.close();
	}
	
	public void disposePackage(){
		packageFile.delete();
	}
	
	public String getLocalAddress(File peerFile){
		Vector<String> peerList = new PeerList().getPeerList(peerFile.getAbsolutePath());
		String localAddress = "";
		try{
			Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
			while(en.hasMoreElements()){
				NetworkInterface i = (NetworkInterface) en.nextElement();
				for (Enumeration<InetAddress> en2 = i.getInetAddresses(); en2.hasMoreElements();) {
					InetAddress addr = (InetAddress) en2.nextElement();
					if (!addr.isLoopbackAddress()) {
						if (addr instanceof Inet4Address && peerList.contains(addr.toString().replace("/", ""))) {
							localAddress = addr.toString().replace("/", "");
						}else{
							continue;
						}
					}
				}
			}
			return localAddress;
		}catch(Exception e){
			e.printStackTrace();
			return localAddress;
		}
	}
}
